#include "classmethod.h"
#include "uml/class/umlclassitem.h"

ClassMethod::ClassMethod(QObject *parent) : QAbstractTableModel(parent), QItemDelegate(parent)
{
    this->_itemTypeFlags &= ~UmlClassAttributeType;
    this->_itemTypeFlags |= UmlClassMethodeType;

    this->_isAbstract = false;

    this->_name = "Method";
}

ClassMethod::~ClassMethod()
{
    qDebug() << "deleting classMethod: " <<  this->_name.toLocal8Bit();
    foreach(ClassAttribute *att, this->_args) {
        delete att;
    }
}

bool ClassMethod::isAbstract()
{
    return this->_isAbstract;
}

void ClassMethod::setIsAbstract(bool isAbstract)
{
    this->_isAbstract = isAbstract;
    this->updateUmlText();
}

void ClassMethod::addAtt(ClassAttribute *att)
{
    this->_args.append(att);
    this->updateUmlText();
}

void ClassMethod::removeAtt(ClassAttribute *delArg)
{
    this->_args.removeOne(delArg);
    this->updateUmlText();
}

bool ClassMethod::hasArguments()
{
    return !this->_args.isEmpty();
}

void ClassMethod::setVisibility(QString visibility)
{
    if (visibility.toLower() == "public") {
        this->_visibility = PUBLIC;
    }

    if (visibility.toLower() == "private") {
        this->_visibility = PRIVATE;
    }

    if (visibility.toLower() == "protected") {
        this->_visibility = PROTECTED;
    }

    this->updateUmlText();
}


QList<ClassAttribute*> ClassMethod::argList()
{
    return this->_args;
}


int ClassMethod::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return this->_args.count();
}

int ClassMethod::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return 2;
}

QVariant ClassMethod::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole || orientation == Qt::Horizontal) {

        if (section == 0) {
            return QObject::tr("Datatype");
        }

        if (section == 1) {
            return QObject::tr("Label");
        }
    }

    return QVariant();
}

QVariant ClassMethod::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) {
        return QVariant();
    }

    if (role == Qt::DisplayRole) {

        int col = index.column();
        int row = index.row();

        ClassAttribute *att = this->_args.at(row);

        if (col == 0) {
            return att->returnType();
        }

        if (col == 1) {
            return att->name();
        }
    }

    return QVariant();
}

Qt::ItemFlags ClassMethod::flags(const QModelIndex & index) const
{
    if (!index.isValid()) {
        return Qt::NoItemFlags;
    }

    if (index.column() == 0 || index.column() == 1) {
        return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
    }

    return Qt::NoItemFlags;
}

void ClassMethod::appendArg(ClassAttribute *att)
{
    if (this->_args.contains(att)) {
        return;
    }

    int row = this->_args.count();

    this->beginInsertRows(QModelIndex(), row, row);
    this->_args.append(att);
    this->endInsertRows();
    this->updateUmlText();
}

void ClassMethod::removeArg(QModelIndex index)
{
    int row = index.row();

    this->beginRemoveRows(QModelIndex(), row, row);
    this->_args.removeAt(row);
    this->endRemoveRows();
    this->updateUmlText();
}

void ClassMethod::moveArg(QModelIndex index, ClassMethod::ArgumentMovement movement) {
    if ((index.row() == 0 && movement == MoveUp) || (index.row() == this->_args.count() - 1 && movement == MoveDown)) {
        return;
    }

    int row = index.row();
    int swap = movement == MoveUp ? row - 1 : row + 1;

    this->_args.swap(row, swap);

    QModelIndex rowIndex = this->createIndex(row, 0);
    QModelIndex swapIndex = this->createIndex(swap, 1);

    this->dataChanged(rowIndex, swapIndex);
    this->updateUmlText();
}

QWidget* ClassMethod::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option);

    if (index.isValid() && parent) {

        if (index.column() == 0) {
            qDebug() << "creating editor";
            QComboBox *dataCombo = new QComboBox(parent);
            dataCombo->setModel(this->_classItem->_project->dataTypes());

            return dataCombo;
        }

        if (index.column() == 1) {
            return new QLineEdit(parent);
        }
    }

    return 0;
}

void ClassMethod::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (index.isValid()) {
        int column = index.column();
        int row = index.row();
        ClassAttribute *att = this->_args.at(row);

        if (column == 0) {
            qDebug() << "setting editor";
            QComboBox *dataCombo = static_cast<QComboBox*>(editor);
            DatatypeDictionary *datatypes = this->_classItem->_project->dataTypes();
            int index = datatypes->indexByLabel(att->returnType());
            dataCombo->setCurrentIndex(index);
        }

        if (column == 1) {
            QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
            lineEdit->setText(att->name());
        }
    }
}

void ClassMethod::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    Q_UNUSED(model);

    if (index.isValid()) {
        int column = index.column();
        int row = index.row();
        ClassAttribute *att = this->_args.at(row);

        if (column == 0) {
            QComboBox *dataCombo = static_cast<QComboBox*>(editor);

            att->setReturnType(dataCombo->currentText());
        }

        if (column == 1) {
            QLineEdit *lineEdit = static_cast<QLineEdit*>(editor);

            att->setText(lineEdit->text());
        }
    }
}

void ClassMethod::openEditor()
{
    this->_classItem->openMethodEditor(this);
}

void ClassMethod::remove()
{
    this->_classItem->removeMeth(this);
    this->_classItem->update();
}

void ClassMethod::toggleIsAbstract()
{
    this->setIsAbstract(!this->isAbstract());
    this->_classItem->updateBoundingBox();
    this->_classItem->update();
}

void ClassMethod::updateUmlText()
{
    QStringList argList;
    foreach (ClassAttribute *att, this->_args) {
        argList << att->umlText();
    }

    this->_umlText = QString("%1 %2( %3 ) : %4")
            .arg(this->umlVisibility())
            .arg(this->_name)
            .arg(argList.join(", "))
            .arg(this->_type);

    if (this->isAbstract()) {
        this->_umlText = "{ " + this->_umlText + " }";
    }

    this->setHtml(this->_umlText);
}
